﻿using Furion.CMS.Core;
using Furion.CMS.Domain;
using Furion.DatabaseAccessor;
using Furion.DataEncryption.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Furion.CMS.Application.Dto
{
    /// <summary>
    /// 登录状态
    /// </summary>
    public class LoginStatus : Enumeration<int>
    {
        /// <summary>
        /// 成功
        /// </summary>
        public static readonly LoginStatus Success = new(1, "ok");
        /// <summary>
        /// 错误
        /// </summary>
        public static readonly LoginStatus Error = new(2, "error");

        public LoginStatus(int id, string name)
            : base(id, name)
        {
        }
    }

    /// <summary>
    /// 登录参数
    /// </summary>
    public class LoginParams : System.Dynamic.DynamicObject
    {
        /// <summary>
        /// 登录类型
        /// </summary>
        /// <example>account</example>
        public string Type { get; set; }
        /// <summary>
        /// 是否自动登录
        /// </summary>
        public bool AutoLogin { get; set; }

        // The inner dictionary.
        private Dictionary<string, object> dictionary = new Dictionary<string, object>();
        // If you try to get a value of a property
        // not defined in the class, this method is called.
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            // Converting the property name to lowercase
            // so that property names become case-insensitive.
            string name = binder.Name.ToLower();

            // If the property name is found in a dictionary,
            // set the result parameter to the property value and return true.
            // Otherwise, return false.
            return dictionary.TryGetValue(name, out result);
        }

        // If you try to set a value of a property that is
        // not defined in the class, this method is called.
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            // Converting the property name to lowercase
            // so that property names become case-insensitive.
            dictionary[binder.Name.ToLower()] = value;

            // You can always add a value to a dictionary,
            // so this method always returns true.
            return true;
        }

        public bool TryGetValue(string key, out object value)
        {
            return dictionary.TryGetValue(key, out value);
        }

        public Dictionary<string, object> GetAll()
        {
            dictionary.TryAdd(nameof(Type), Type);
            dictionary.TryAdd(nameof(AutoLogin), AutoLogin);

            return dictionary;
        }

        public UserLoginRecord ToLoginRecord(HttpContext context)
        {
            // 获取登陆者访问信息
            string remoteIp = context.GetRemoteIpAddressToIPv4(),
                refererUrl = context.Request.GetRefererUrlAddress();
            context.Request.Headers.TryGetValue("user-agent", out StringValues userAgent);

            LoginVerifyType.TryFromNamed(Type, out LoginVerifyType verifyType);

            return new UserLoginRecord
            {
                Account = verifyType?.GetAccount(this),
                Host = remoteIp,
                RefererUrl = refererUrl,
                UserAgent = userAgent,
                PostData = this.GetAll()
            };
        }
    }

    /// <summary>
    /// 双因素验证参数
    /// </summary>
    public class TwoFactorVerifyParams
    {
        /// <summary>
        /// 手机号码
        /// </summary>
        public string PhoneNumber { get; set; }
        /// <summary>
        /// 短信验证码
        /// </summary>
        public string Captcha { get; set; }
    }

    /// <summary>
    /// 登录结果
    /// </summary>
    public class LoginResult
    {
        public LoginResult(string type, LoginStatus status)
        {
            Type = type;
            Status = status.ToString();
            ResultMessage = "账户或密码错误";
        }

        /// <summary>
        /// 登录类型
        /// </summary>
        public string Type { get; set; }
        /// <summary>
        /// 登录状态
        /// </summary>
        public string Status { get; set; }
        /// <summary>
        /// 结果消息
        /// </summary>
        public string ResultMessage { get; set; }
        /// <summary>
        /// 启用了双因素验证后要进行手机短信验证
        /// </summary>
        public string VerifyPhoneNumber { get; set; }

        /// <summary>
        /// 出错
        /// </summary>
        /// <param name="message">错误消息</param>
        public void OnError(string message)
        {
            Status = LoginStatus.Error.ToString();
            ResultMessage = message;
        }

        /// <summary>
        /// 成功
        /// </summary>
        public void OnSuccess()
        {
            Status = LoginStatus.Success.ToString();
            ResultMessage = "";
        }

        /// <summary>
        /// 启用了双因素验证的结果
        /// 验证成功,但是需要进行进一步身份验证
        /// </summary>
        public void OnTwoFactorVerify(string phoneNumber)
        {
            Status = LoginStatus.Success.ToString();
            ResultMessage = "";
            VerifyPhoneNumber = phoneNumber;
        }
    }
}
